package com.smsc.headend.task.engine.service.kms.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Tuple;
import cn.hutool.json.JSONUtil;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.smsc.headend.common.utils.RedisKeys;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.dmo.MeterConfigDMO;
import com.smsc.headend.module.asset.entity.*;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.kms.KmsService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Description
 * @Author W26102
 * @Date 2021/8/10 16:14
 * @Sine 1.0
 **/
@Service
@Slf4j
public class KmsServiceImpl implements KmsService {

    public static final String GET_SECURITY_KEYS = "GetSecurityKeysMult";
    public static final String UTF_8 = "UTF-8";
    @Autowired
    AssetService assetService;

    @Autowired
    FeignAssetManagementService feignAssetManagementService;

    @Autowired
    RedisUtils redisUtils;

    @Override
    @SneakyThrows
    public DeviceSAPandKeys getMeterCommunicationKeyForHls(Long meterId, Integer roleId) {
        String kmsEnable = assetService.getSystemProperty("KMS_ENABLE");

        DeviceSAPandKeys deviceSAPandKeys = feignAssetManagementService.getMeterCommunicationKey(meterId, roleId);
        Preconditions.checkNotNull(deviceSAPandKeys);

        MeterConfigDMO meterConfigDMO = feignAssetManagementService.getMeterConfigById(meterId);
        Preconditions.checkNotNull(meterConfigDMO);

        if (StringUtils.isEmpty(kmsEnable) || Boolean.valueOf(kmsEnable).equals(Boolean.FALSE)) {
            return getLocalDeviceSAPandKeys(roleId, deviceSAPandKeys, meterConfigDMO);
        }

        return getKmsDeviceSAPandKeys(meterId, roleId, deviceSAPandKeys, meterConfigDMO);
    }

    private DeviceSAPandKeys getKmsDeviceSAPandKeys(Long meterId, Integer roleId, DeviceSAPandKeys deviceSAPandKeys, MeterConfigDMO meterConfigDMO) throws UnsupportedEncodingException {
        String url = assetService.getSystemProperty("KMS_API_URL");
        String accessKey = assetService.getSystemProperty("KMS_ACCESS_KEY");
//        String accessSecret = assetService.getSystemProperty("KMS_ACCESS_SECRET");
//        Boolean signature = Boolean.valueOf(assetService.getSystemProperty("KMS_SIGNATURE_SWITCH"));

        Meter meter = assetService.getMeterById(meterId);
        Preconditions.checkNotNull(meter);

        String roleName = feignAssetManagementService.getDeviceRoleName(Long.valueOf(roleId));
        Preconditions.checkNotNull(roleName);

        ComEntity comEntity = assetService.getComById(meterConfigDMO.getComId());
        Preconditions.checkNotNull(comEntity);
        Dcu dcuEntity = feignAssetManagementService.getDcuById(comEntity.getDeviceId());
        Preconditions.checkNotNull(dcuEntity);

        List<Tuple> ids = Lists.newArrayList(new Tuple(meter.getMeterId(), meter.getModelId(), meter.getCustomerSn(), "meter"),
                new Tuple(dcuEntity.getDcuId(), dcuEntity.getModelId(), dcuEntity.getCustomerSn(), "dcu"));
        List<Map> bizContentList = Lists.newArrayList();
        for (Tuple id : ids) {
            DeviceTypeGroup deviceTypeGroup = redisUtils.getDeviceTypeGroup(id.get(1), null, feignAssetManagementService);;
            Preconditions.checkNotNull(deviceTypeGroup);

            Map bizContentMeterMap = Maps.newHashMap();
            bizContentMeterMap.put("manufacture", deviceTypeGroup.getManufacturerName());
            bizContentMeterMap.put("className", deviceTypeGroup.getModelName());
            bizContentMeterMap.put("serialNumber", (String) id.get(2));
            bizContentMeterMap.put("logicObjectName", roleName);
            bizContentMeterMap.put("bizContentId", String.valueOf((Long) id.get(0)));
            bizContentList.add(bizContentMeterMap);
        }

        String bizContent = JSONUtil.toJsonStr(bizContentList);
        UriComponentsBuilder build = UriComponentsBuilder.fromHttpUrl(url)
                .queryParam("Action", GET_SECURITY_KEYS)
                .queryParam("AccessKeyId", accessKey)
                .queryParam("BizContent", URLEncoder.encode(bizContent, UTF_8))
                .queryParam("Timestamp", URLEncoder.encode(DateUtil.format(new Date(), DatePattern.UTC_FORMAT), UTF_8)
                );
        log.info("request kms info:{}", build.build().toUriString());
        RestTemplate restTemplate = new RestTemplate();
        GetSecurityKeysResponse getSecurityKeysResponse = restTemplate.getForObject(build.build().toUri(), GetSecurityKeysResponse.class);
        log.info("response kms info:{}", JSONUtil.toJsonStr(getSecurityKeysResponse));
        Preconditions.checkNotNull(getSecurityKeysResponse);
        Preconditions.checkArgument(getSecurityKeysResponse.getMsg().equalsIgnoreCase("success"));
        Preconditions.checkNotNull(getSecurityKeysResponse.getBizContent());

        SecurityHls securityHls = new SecurityHls();
        List<BizContent> bizContents = JSONUtil.toList(JSONUtil.parseArray(getSecurityKeysResponse.getBizContent()), BizContent.class);
//        Preconditions.checkArgument(bizContents.size() == 2);

        for (BizContent returnContent : bizContents) {
            for (Tuple id : ids) {
                if (returnContent.getBizContentId().equals(String.valueOf((Long) id.get(0))) && ((String) id.get(3)).equals("meter")) {
                    securityHls.setGak(returnContent.getGak());
                    securityHls.setGuek(returnContent.getGuek());
                } else if (returnContent.getBizContentId().equals(String.valueOf((Long) id.get(0))) && ((String) id.get(3)).equals("dcu")) {
                    securityHls.setKek(returnContent.getKek());
                }
            }
        }
        securityHls.setRoleId(Long.valueOf(roleId));
        securityHls.setDeviceId(meterId);
        if (StringUtils.isNotBlank(securityHls.getGak()) && StringUtils.isNotBlank(securityHls.getGuek())) {
            deviceSAPandKeys.setHlsKeys(Lists.newArrayList(securityHls));
        }
        return deviceSAPandKeys;
    }

    private DeviceSAPandKeys getLocalDeviceSAPandKeys(Integer roleId, DeviceSAPandKeys deviceSAPandKeys, MeterConfigDMO meterConfigDMO) {
        if(CollUtil.isEmpty(deviceSAPandKeys.getHlsKeys())) {
            return deviceSAPandKeys;
        }
        Preconditions.checkArgument(deviceSAPandKeys.getHlsKeys().size() == 1);
        DeviceSAPandKeys comDeviceSapandKeys = feignAssetManagementService.getTerminalCommunicationKey(meterConfigDMO.getComId(), roleId);
        Preconditions.checkNotNull(comDeviceSapandKeys);

        deviceSAPandKeys.getHlsKeys().get(0).setKek(CollUtil.isEmpty(comDeviceSapandKeys.getHlsKeys()) ? null : comDeviceSapandKeys.getHlsKeys().get(0).getKek());
        return deviceSAPandKeys;
    }

    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public static class GetSecurityKeysResponse {
        private String msg;
        private Integer code;
        private String bizContent;
    }

    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public static class BizContent {
        private String gak;
        private String guek;
        private String kek;
        private String roleName;
        private String bizContentId;
    }


}
